package com.nika.usercenter.job;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.nika.usercenter.model.domain.User;
import com.nika.usercenter.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.ParameterResolutionDelegate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.sql.Time;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 缓存预热任务
 * 分布式锁
 * @author nika
 */

//@Component
@Slf4j
public class PreCacheJob {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private UserService userService;

    @Autowired
    private RedissonClient redissonClient;
    
    private List<Long> userIdList= Arrays.asList(11L);

    /**
     * 包含了看门狗机制
     *  开一个监听线程，如果方法还没有执行完，就要到了废弃时间，就帮你自动续期
     */

    //指定cron 表达式或者 (秒 分 时 日 周 年)
    @Scheduled(cron = "0 03 21 * * *")
    public void doCacheRecommendUser(){
        RLock lock = redissonClient.getLock("nika:usercenter:preCacheJob:recommend:lock");
        ValueOperations redisOperations = redisTemplate.opsForValue();
        try {
            //如果获取到锁
            //tryLock,尝试获取锁，第一个参数是在外面的没有执行任务的线程的等待时间，这里设置为0，因为我们只需要一台服务器去执行任务
            //每个进程来一次，拿到了就执行，拿不到就自己润
            //第二个参数释放时间：锁的过期时间，超过一段时间没有执行完会释放锁
            if(lock.tryLock(0,30000L,TimeUnit.MILLISECONDS)){
                System.out.println("getLock:"+Thread.currentThread().getId());
                for (Long userId : userIdList) {
                    String redisKey = String.format("nika-usercenter-userId:%s",userId);
                    Page<User> userPage = userService.page(new Page<>(2, 10), new QueryWrapper<>());
                    try {
                        redisOperations.set(redisKey,userPage,40, TimeUnit.MINUTES);
                    } catch (Exception e) {
                        log.error("redisKey set error",e);
                    }
                }
            }
        } catch (InterruptedException e) {
            log.error("doCacheRecommend error");
        }finally {
            /**
             *判断这个锁是否是当前这个线程加的锁
             *为什么要判断？因为如果一个线程运行40s，但是过期时间设置为30s，那么在第30s的时候原本的nika:usercenter:preCacheJob:recommend:lock=A
             *变成了nika:usercenter:preCacheJob:recommend:lock=B，如果不判断直接lock.unlock，那么这个B线程的锁就会被A解锁，此时C也就开始进行了
             *它的任务。不满足同一时间只有一个线程在执行任务的定义，加锁失去意义。因此，我们需要在解锁的时候判断，解的这个锁是否是当前这个线程的锁，如果不是
             *就不解锁。
             *加锁需要遵循两大原则：1.运行完释放锁（释放空间）2.设置过期时间（防止运行一半服务器挂掉，那么这个锁就永远不会被解锁）
             */
            if(lock.isHeldByCurrentThread()){
                System.out.println("unlock"+Thread.currentThread().getId());
                lock.unlock();
            }
        }

        
    }

}
